#include "gtlGUI.h"
#include <GTL/gtlFiles.h>
#include <GTL/gtlStructures.h>
#include <GTLEngine/gtlGame.h>
#include <GTL/gtlAutomember.h>
#include <GTL/gtlSystem.h>

using GTL::Structures::DataNode;

namespace GTL { namespace Engine { namespace GUI {

	///************************************************************************/
	/// Statics, unfortunately
	///************************************************************************/

	static Control* TextFocus = 0;

	///************************************************************************/
	///* Control                                                              */
	///************************************************************************/

	Control::Control() : X(0), Y(0), Width(0), Height(0),
		Visible(true), Enabled(true), MouseChildren(true),
		Color(1,1,1,1), InputMask(false), TheImage(),

		mFlags(0), mIsDynamic(false), mParent(0), mChildren(),
		
		mOver(false), mPressed(false), mEvents(0)
	{
		Userdata[0] = 0;
		Userdata[1] = 0;
		Userdata[2] = 0;
		Userdata[3] = 0;
	}

	void Control::BuildFrom( DataNode* node )
	{
    node->GetElementIfExists("x",&X);
		node->GetElementIfExists("y",&Y);
		node->GetElementIfExists("width",&Width);
		node->GetElementIfExists("height",&Height);

    node->GetElementIfExists("visible", &Visible);
		node->GetElementIfExists("enabled", &Enabled);
		node->GetElementIfExists("mouse_children", &MouseChildren);

    node->GetElementIfExists("name", &Name);

		node->GetElementIfExists("group", &Group);

    const DataNode* im = node->GetElement("color");
		if (im)
			NodeToObject(Color, im);
      //Color = ColorRGBA(im);

		im = node->GetElement("input_mask");
		if (im)
		{
			InputMask = true;
			//InputMaskRect = NodeToRect(im);
			NodeToObject(InputMaskRect, im);
		}

		im = node->GetElement("image");
		if (im)
		{
			NodeToObject(TheImage, im);
		}
	}

	Control::~Control()
	{
		if (TextFocus == this) TextFocus = 0;

		DestroyChildren();
	}

	void Control::DestroyChildren()
	{
		for (size_t i=0; i<mChildren.size(); i++)
		{
			mChildren[i]->DestroyChildren();
			if (mChildren[i]->mIsDynamic)
			{
				delete mChildren[i];
			}
		}
		mChildren.clear();
	}


	int Control::GetChildIndex(const Control* obj) const
	{
		if (obj->GetParent() != this) return -1;

		for (uint32_t i=0; i<mChildren.size(); i++)
		{
			if (mChildren[i] == obj)
				return i;
		}

		return -1;
	}

	Control* Control::GetControlAtPoint(float x, float y, bool onlyEnabled)
	{
		if (onlyEnabled && Enabled == false) return 0;

		if (!onlyEnabled || (onlyEnabled && MouseChildren))
		{
			for (int i=int(mChildren.size())-1; i>=0; --i)
			{
				Control* child = mChildren[i]->GetControlAtPoint(x-X, y-Y, onlyEnabled);
				if (child) return child;
			}
		}

		if (!IsPointInside(x,y,true)) return 0;

		return this;
  }

  Control* Control::GetControlWithName( const char* name )
  {
    if (name == Name) return this;

    for (size_t i=0; i<mChildren.size(); i++)
    {
      Control* child = mChildren[i]->GetControlWithName(name);
      if (child) return child;
    }
    return 0;
  }

	int Control::AddChild(Control& obj)
	{
		return AddChild(&obj);
	}

	int Control::AddChild(Control* obj)
	{
		if (!obj) return -1;
		if (obj->GetParent() == this) return GetChildIndex(obj);

		if (obj->GetParent())
			obj->GetParent()->RemoveChild(obj);
		mChildren.push_back(obj);

		obj->Attached(this);

		return int(mChildren.size())-1;
	}

	int Control::AddToParent(Control& obj)
	{
		return obj.AddChild(this);
	}
	int Control::AddToParent(Control* obj)
	{
		return obj->AddChild(this);
	}

	Control* Control::RemoveChildAt( size_t at )
	{
		eastl::vector<Control*>::iterator iter = mChildren.begin()+at;
		if (iter != mChildren.end())
		{
			Control* obj = *iter;
			obj->Unattached();
			mChildren.erase(iter);
			return obj;
		} else
		{
			return 0;
		}
	}

	Control* Control::RemoveChild(Control* obj)
	{
		if (obj->GetParent() != this) return 0;

		for (eastl::vector<Control*>::iterator iter = mChildren.begin(); iter!=mChildren.end(); ++iter)
		{
			if (*iter == obj)
			{
				obj->Unattached();
				mChildren.erase(iter);
				return obj;
			}
		}

		return 0;
	}

	void Control::Attached(Control* parent)
	{
		mParent = parent;
		//obj->mEvents = parent->mEvents;
	}

	void Control::Unattached()
	{
		mParent = 0;
		//obj->mEvents = null;
	}

	uint32_t Control::Update(float dt)
	{
		UpdateAt(dt);

		size_t numChildren = mChildren.size();
		for (size_t i=0; i<numChildren; i++)
		{
			mChildren[i]->Update(dt);
		}

		return 0;
	}

	uint32_t Control::Draw(float x, float y, const ColorRGBA& color)
	{
		if (!Visible) return 0;

		ColorRGBA newColor = (color*Color);

		if (TheShader)
			TheShader->Use();
		else 
			Backend->UnuseShader();
		DrawAt(X+x, Y+y, newColor);
		
		size_t numChildren = mChildren.size();
		for (size_t i=0; i<numChildren; i++)
		{
			mChildren[i]->Draw(X+x, Y+y, newColor);
		}

		return 0;
	}

	void Control::UpdateAt(float dt)
	{
	}

	void Control::DrawAt(float x, float y, const ColorRGBA& color)
	{
		if (TheImage) TheImage->Draw(Rect(x, y, x+Width, y+Height), color);
	}

	bool Control::IsPointInside( float x, float y, bool applyInputMask /*= false*/ ) const
	{
		if (!applyInputMask || (applyInputMask && !InputMask))
			return (x>=X && y>=Y && x<=int(X+Width) && y<=int(Y+Height));
		else
			return InputMaskRect.Contains(x,y);
	}

	Rect Control::GetRect() const
	{
		return Rect(X,Y,X+Width,Y+Height);
	}

	bool Control::MouseMove( float x, float y )
	{
		if (!Enabled) return false;

		bool isMouseOver = true;

		if (MouseChildren)
		{
			for (ChildrenContainer::reverse_iterator i = mChildren.rbegin(); i != mChildren.rend(); ++i) // mouse events are front to back
			{
				Control* control = *i;
				if (control->MouseMove(x-X, y-Y)) // jesli dziecko obsluzylo
					isMouseOver = false;
			}
		}
		if (!IsPointInside(x,y,true)) 
			isMouseOver = false;

		if (mOver && !isMouseOver)
		{
			mOver = false;
			OnMouseOut(x-X, y-Y);
		}
		else if (!mOver && isMouseOver)
		{
			mOver = true;
			OnMouseOver(x-X, y-Y);
		}
		return isMouseOver;
	}

	bool Control::MouseDown(float x, float y)
	{
		if (!Enabled) return false;

		if (MouseChildren)
		{
			for (ChildrenContainer::reverse_iterator i = mChildren.rbegin(); i != mChildren.rend(); ++i) // mouse events are front to back
			{
				Control* control = *i;
				if (control->MouseDown(x-X, y-Y)) // jesli dziecko obsluzylo
					return true;
			}
		}
		if (!IsPointInside(x,y,true)) return false;

		if (OnMouseDown(x-X, y-Y))
			return true; // obsluzone
		else
			return false; // nieobsluzone
	}

	bool Control::MouseUp(float x, float y)
	{
		if (!Enabled) return false;

		if (MouseChildren)
		{
			for (ChildrenContainer::reverse_iterator i = mChildren.rbegin(); i != mChildren.rend(); ++i) // mouse events are front to back
			{
				Control* control = *i;
				control->MouseUp(x-X, y-Y);
			}
		}

		OnMouseUp(x, y);

		return true;
	}

	bool Control::KeyUp( int key )
	{
		if (!Enabled) return false;

		if (MouseChildren)
		{
			for (ChildrenContainer::reverse_iterator i = mChildren.rbegin(); i != mChildren.rend(); ++i) // mouse events are front to back
			{
				Control* control = *i;
				control->KeyUp(key);
			}
		}

		OnKeyUp(key);

		return true;
	}

	bool Control::KeyDown( int key )
	{
		if (!Enabled) return false;

		if (MouseChildren)
		{
			for (ChildrenContainer::reverse_iterator i = mChildren.rbegin(); i != mChildren.rend(); ++i) // mouse events are front to back
			{
				Control* control = *i;
				if (control->KeyDown(key)) // jesli dziecko obsluzylo
					return true;
			}
		}

		return OnKeyDown(key);
	}

	bool Control::CharEntered( int key, int ch )
	{
		if (!Enabled) return false;

		if (MouseChildren)
		{
			for (ChildrenContainer::reverse_iterator i = mChildren.rbegin(); i != mChildren.rend(); ++i) // mouse events are front to back
			{
				Control* control = *i;
				if (control->CharEntered(key, ch)) // jesli dziecko obsluzylo
					return true;
			}
		}

		return OnChar(key, ch);
	}

	bool Control::OnMouseOver( float x, float y )
	{
		AddEvent(GameEvent::Make(Event::MouseOver, this, Point(x, y)));
		return true;
	}

	bool Control::OnMouseOut( float x, float y )
	{
		AddEvent(GameEvent::Make(Event::MouseOut, this, Point(x, y)));
		return true;
	}

	bool Control::OnKeyDown( int key )
	{
		return false;
	}

	bool Control::OnKeyUp( int key )
	{
		return false;
	}

	bool Control::OnChar( int key, int ch )
	{
		return false;
	}

	bool Control::OnMouseDown( float x, float y )
	{
		if (Enabled)
		{
			AddEvent(GameEvent::Make(Event::MouseDown, this, Point(x, y)));
			mPressed = true;
			return true;
		} else return false;
	}

	bool Control::OnMouseUp( float x, float y )
	{
		if (mPressed && IsPointInside(x, y, true))
		{
			AddEvent(GameEvent::Make(Event::MouseUp, this, Point(x, y)));
			mPressed = false;
			return OnClick();
		}

		mPressed = false;
		return false;
	}

	bool Control::OnClick()
	{
		AddEvent(GameEvent::Make(Event::Click, this));
		return true;
	}

	void Control::AddEvent( const GameEvent& event )
	{
		if (this->HandleEvent(event))
			return;

		if (mEvents)
		{
			mEvents->PushEvent(event);
		} else if (mParent)
		{
			mParent->AddEvent(event);
		}
	}

	bool Control::HandleEvent( const GameEvent& event )
	{
		return false;
	}

	Control& Control::Set( float x, float y, const Image& image, bool size_from_image /*= true*/ )
	{
		X = x;
		Y = y;
		TheImage = image;
		if (image && size_from_image)
		{
			Width = image->Width;
			Height = image->Height;
		}
		return *this;
	}

	Control* Control::CreateControlFromNode( GTL::Structures::DataNode* node, Control::ClassMap* classes )
  {
    if (!node || node->Type != DataNode::DICT) return 0;

    /// Create specific control type
    Control* ctrl = 0;
		const eastl::string controlType = GTL::Strings::ToLower(node->GetElementAsString("type"));
    if (controlType == "control")
    {
      ctrl = new Control;
		} 
		else if (controlType == "button")
		{
			ctrl = new Button;
		} 
		else if (controlType == "label")
		{
			ctrl = new Label;
		} 
		else if (controlType == "editbox")
		{
			ctrl = new EditBox;
		} 
		else if (controlType == "textbox")
		{
			ctrl = new TextBox;
		} 
		else
		{
			const eastl::string qualifiedControlType = eastl::string("gui/")+controlType;
			ctrl = (Control*)GTL::Producer::ProduceType(qualifiedControlType.c_str());
		}

    if (ctrl)
    {
      /// We have to make sure it's actually deleted
      ctrl->mIsDynamic = true;

      /// If the control has a "class" property, build the properties from the class first
      const DataNode* className = node->GetElement("class");
      if (classes && className && className->Type == DataNode::STRING)
      {
        DataNode::DictType::iterator classDefinition = classes->find(className->String);
        if (classDefinition != classes->end())
        {
          ctrl->BuildFrom(classDefinition->second);
        }
      }

      /// Build the control properties from the actual defining node second, so that the node props
      /// overrides the class defaults
      ctrl->BuildFrom(node);

      /// If the node has "classes" they can be used as classes for the child controls
      Control::ClassMap classes_map;
      const DataNode* classesNode = node->GetElement("classes");
      if (classesNode && classesNode->Type == DataNode::DICT)
      {
        if (!classes)
        {
          classes = &classes_map;
        }

        classes->insert(classesNode->Dict.begin(), classesNode->Dict.end());
      }

      /// Build children
      if (const DataNode* children = node->GetElement("children"))
      {
        if (children->Type == DataNode::LIST)
        {
          for (DataNode::ListType::const_iterator i=children->List.begin(); i!=children->List.end(); ++i)
          {
            Control* child = CreateControlFromNode(*i, classes);
            if (child)
            {
              ctrl->AddChild(child);
              /// TODO: Run any actions here (align to parent, etc.)

            }
          }
        }
      }
    } else
		{
			Backend->LogWarning("No GUI control type for `%s`", node->GetElementAsString("type").c_str());
		}

    return ctrl;
  }

	Control* Control::CreateControlFromFile( const char* filename, int fileformat /*= GTL::Formats::AutoDetect::FormatID*/, Control::ClassMap* classes )
	{
		if (fileformat == GTL::Formats::AutoDetect::FormatID)
			fileformat = GTL::Formats::DetectFormatFromFilename(filename);
		if (!fileformat) return 0;

		GTL::Structures::DataNode* node = GTL::Formats::GetNewNode(fileformat);
		GTL_SCOPE_DEFER_DELETE(DataNode, node);
		char* tf = GTL::Files::Just::LoadEntireTextFile(filename);
		
		if (!tf) Backend->LogError("Could not load Control file `%s`", filename);

		node->Parse(tf);
		delete[] tf;
		return GUI::Control::CreateControlFromNode(node, classes);
	}

	Control* Control::CreateControlFromWilsonFile( const char* filename, Control::ClassMap* classes )
	{
		return CreateControlFromFile(filename, 'wils', classes);
	}

	Control* Control::GetControlByPath( const char* path )
	{
		eastl::vector<eastl::string> path_fragments;
		GTL::Strings::SplitMultiple(path, "/", path_fragments);
		Control* cur_control = this;
		for (size_t i=0; i<path_fragments.size(); i++)
		{
			cur_control = cur_control->GetControlWithName(path_fragments[i].c_str());
			if (!cur_control) return 0;
		}
		return cur_control;
	}

	Point Control::GetPosition() const
	{
		return Point(X, Y);
	}

	///************************************************************************/
	///* Button                                                               */
	///************************************************************************/

	Button::Button() : mFont(), TextColor(1,1,1,1), mType(ButtonType::Normal), mLabel(0), mLabelLen(0), mActive(false)
	{
	}

	int Button::GetImageState()
	{
		if (!Enabled) return ButtonState::Disabled;
		if (mPressed) return ButtonState::Pressed;
		if ( mActive) return ButtonState::Selected;
		if (   mOver) return ButtonState::MouseOver;
		return ButtonState::Normal;
	}

	void Button::SetType(int type)
	{
		mType = type;
	}

	void Button::SetLabel( const char* label )
	{
		mLabel = label;
		if (mFont && mLabel)
			mLabelLen = mFont->GetTextWidth(label);
	}

	void Button::SetFont(const Font& font)
	{
		mFont = font;
		if (mFont && mLabel)
			mLabelLen = mFont->GetTextWidth(mLabel);
	}

	Button& Button::Set(float x, float y, const Image& image, const Font& font, const char* label, int type/* = ButtonType::Normal*/)
	{
		X = x;
		Y = y;
		TheImage = image;
		mFont = font;
		SetLabel(label);
		mType = type;

		Width = TheImage->Width;
		Height = TheImage->Height/ButtonState::TotalStates;

		return *this;
	}


	void Button::DrawAt(float x, float y, const ColorRGBA& color)
	{
		if (TheImage)
		{
			TheImage->Draw(Rect(0, GetImageState()*Height, Width, GetImageState()*Height+Height), Rect(x, y, x+Width, y+Height), color);
		}
		if (mLabel && mFont && *mLabel)
		{
			int off = mPressed?1:0;
			mFont->DrawText(Point(off+x+Width/2-mLabelLen/2, off+y+(Height/2)-(mFont->GetHeight()/2)), mLabel, color*TextColor);
		}
	}

	bool Button::OnClick()
	{
		AddEvent(GameEvent::Make(Event::Click, this));

		if (mType == ButtonType::CheckButton)
			mActive = !mActive;
		if (mType == ButtonType::RadioButton && mParent)
		{
			for (size_t i=0; i<mParent->GetNumChildren(); i++)
			{
				Control* disp = mParent->GetChildAt(i);
				if (disp->Is('BUTT') || disp->Is('IMBU'))
				{
					Button* b = (Button*)disp;
					if (b->mType == ButtonType::RadioButton && b->Group == Group)
						b->mActive = false;
				}
			}
			mActive = true;
		}

		//mClickSound->play();

		return true;
	}

  void Button::BuildFrom( DataNode* node )
  {
    Control::BuildFrom(node);

    if (const DataNode* font = node->GetElement("font"))
      NodeToObject(mFont, font);//mFont = NodeToFont(font);
    
    if (node->GetElementIfExists("label", &mInternalLabel))
      SetLabel(mInternalLabel.c_str());

    if (node->ElementHasStringValue("buttontype", "check") || node->ElementHasStringValue("buttontype", "checkbutton"))
      mType = ButtonType::CheckButton;
    if (node->ElementHasStringValue("buttontype", "normal"))
      mType = ButtonType::Normal;
    if (node->ElementHasStringValue("buttontype", "radio") || node->ElementHasStringValue("buttontype", "radiobutton"))
      mType = ButtonType::RadioButton;

    if (const DataNode* tc = node->GetElement("textcolor"))
			NodeToObject(TextColor, tc);
			//DataNode::TextColor = ColorRGBA(tc);

		node->GetElementIfExists("active", &mActive);

    if (TheImage)
    {
      Width = TheImage->Width;
      Height = TheImage->Height/ButtonState::TotalStates;
    }
  }

	///************************************************************************/
	///* Label                                                                */
	///************************************************************************/

	Label::Label() : mFont(), TextColor(1,1,1,1), mLabel(0), mLabelLen(0)
	{
	}

	void Label::SetFont( const Font& font )
	{
		mFont = font;
		RecalculateSize();
	}

	void Label::SetLabel( const char* label )
	{
		mLabel = label;
		RecalculateSize();
	}

	Label& Label::Set( float x, float y, const Font& font, const char* label )
	{
		X = x;
		Y = y;
		mFont = font;
		mLabel = label;
		RecalculateSize();

		return *this;
	}

	void Label::BuildFrom( GTL::Structures::DataNode* node )
	{
		Control::BuildFrom(node);

		if (const DataNode* font = node->GetElement("font"))
			NodeToObject(mFont, font);

		if (node->GetElementIfExists("text", &mInternalLabel))
			SetLabel(mInternalLabel.c_str());

		if (const DataNode* tc = node->GetElement("textcolor"))
			NodeToObject(TextColor, tc);
	}

	void Label::DrawAt( float x, float y, const ColorRGBA& color )
	{
		if (mFont && mLabel)
		{
			mFont->DrawText(GetPosition(), mLabel, TextColor*color);
		}
	}

	void Label::RecalculateSize()
	{
		if (mFont && mLabel)
		{
			mLabelLen = mFont->GetTextWidth(mLabel);
			Width = mLabelLen;
			Height = mFont->GetHeight();
		}
	}

	///************************************************************************/
	/// EditBox
	///************************************************************************/


	EditBox::EditBox() : TextColor(ColorRGBA::White()), CursorColor(ColorRGBA::Black()), MaxSize(0), mText(), mCursorPos(0), mLabelLen(0)
	{

	}

	void EditBox::SetFont( const Font& font )
	{
		mFont = font;
		RecalculateSize();
	}

	void EditBox::SetText( const char* text )
	{
		mText = text;
		mCursorPos = 0;
		RecalculateSize();
	}

	EditBox& EditBox::Set( float x, float y, const Image& image, float width, const Font& font, const char* text /*= ""*/ )
	{
		TheImage = image;

		X = x;
		Y = y;
		Width = width;
		mFont = font;
		mText = text;
		mCursorPos = 0;
		RecalculateSize();

		return *this;
	}

	void EditBox::BuildFrom( GTL::Structures::DataNode* node )
	{
		Control::BuildFrom(node);

		if (const DataNode* font = node->GetElement("font"))
			NodeToObject(mFont, font);

		node->GetElementIfExists("text", &mText);

		node->GetElementIfExists("maxsize", &MaxSize);

		if (const DataNode* tc = node->GetElement("textcolor"))
			NodeToObject(TextColor, tc);

		if (const DataNode* tc = node->GetElement("cursorcolor"))
			NodeToObject(CursorColor, tc);

		RecalculateSize();
	}

	void EditBox::DrawAt( float x, float y, const ColorRGBA& color )
	{
		int imst = 0;
		if (TextFocus == this)
			imst = 1;
		if (!Enabled)
			imst = 2;
		
		if (TheImage)
		{
			TheImage->Draw(Rect(0, imst*Height, Width, imst*Height+Height), Rect(x, y, x+Width, y+Height), color);
		}

		if (mFont)
		{
			if (TextFocus == this && (mBlink%1000) < 500)
			{
				float tw = mFont->GetTextWidth(mText.c_str(), (int)mCursorPos)+1;
				Backend->DrawLine(InputArea.P1.X+tw, InputArea.P1.Y, InputArea.P1.X+tw, InputArea.P2.Y, CursorColor);
			}
			mFont->DrawText(InputArea.P1, mText.c_str(), TextColor*color);
		}
	}

	void EditBox::RecalculateSize()
	{
		mTextLength = GTL::Strings::UTF8::GetLength(mText.c_str());

		if (mFont)
		{
			mLabelLen = mFont->GetTextWidth(mText.c_str());
		}

		if (TheImage && mFont)
		{
			Height = TheImage->Height/3;
			Width = TheImage->Width;

			InputArea = Rect(X+4, (Y+Height-4) - (mFont->GetHeight()), X+Width-4, Y+Height-4);
		}
	}

	bool EditBox::OnChar( int key, int ch )
	{
		if (TextFocus != this) return false;

		mBlink = 0;

		if (key == GTL_KEY_BACKSPACE && mTextLength && mCursorPos)
		{
			size_t of = GTL::Strings::UTF8::GetOffset(mText.c_str(), mCursorPos);
			int p = GTL::Strings::UTF8::Prev(mText.c_str()+of, 0);

			mCursorPos--;
			mTextLength--;

			mText.erase(of-p, p);
		}
		else if (key == GTL_KEY_DELETE && mTextLength && mCursorPos != mTextLength)
		{
			size_t of = GTL::Strings::UTF8::GetOffset(mText.c_str(), mCursorPos);
			int p = GTL::Strings::UTF8::Next(mText.c_str()+of, 0);

			//mCursorPos--;
			mTextLength--;

			mText.erase(of, p);
		}
		else if (ch > 31)
		{
			if (!MaxSize || MaxSize > mTextLength)
			{
				size_t of = GTL::Strings::UTF8::GetOffset(mText.c_str(), mCursorPos);

				char uc[4] = {0};
				mText.insert(of, uc, GTL::Strings::UTF8::Encode(ch, uc));

				mTextLength++;
				mCursorPos++;
			}
		} else
		{
			//printf("%i x %i\n", key, ch);
			if (key == (GTL_KEYMOD_CTRL|GTL_KEY_V))
			{
				eastl::string str = GTL::System::GetClipboardUTF8Text();
				size_t len = GTL::Strings::UTF8::GetLength(str.c_str());

				size_t of = GTL::Strings::UTF8::GetOffset(mText.c_str(), mCursorPos);

				mText.insert(of, str);

				mTextLength += len;
				mCursorPos += len;
			}
			if (key == GTL_KEY_LEFT && mCursorPos)
				mCursorPos--;
			else if (key == GTL_KEY_RIGHT && mCursorPos < mTextLength)
				mCursorPos++;
			else if (key == GTL_KEY_HOME)
				mCursorPos = 0;
			else if (key == GTL_KEY_END)
				mCursorPos = mTextLength;
			else if (key || ch)
				AddEvent(GameEvent::Make(Event::KeyPressed, this, GameEvent::MakeCharPair(key, ch)));
		}

		return true;
	}

	bool EditBox::OnMouseDown( float x, float y )
	{
		TextFocus = this;
		mBlink = 0;

		float pos = x - (InputArea.P1.X-X);
		if (pos < 0)
		{
			mCursorPos = 0;
		} else if (pos >= mFont->GetTextWidth(mText.c_str()))
		{
			mCursorPos = mTextLength;
		} else
		{
			/*
			int st = mTextLength/2;
			int move = st/2;
			while (move)
			{
				float len = mFont->GetTextWidth(mText.c_str(), st);
				if (len > pos)
					st -= move;
				else
					st += move;
				move /= 2;

				if (st <= 0 || st >= mTextLength)
					break;
			}*/
			size_t i = 0;
			float prev_len = 0;
			for (i=0; i<mTextLength; i++)
			{
				float len = mFont->GetTextWidth(mText.c_str(), i);
				if (pos < len+(len-prev_len)/2)
					break;
				prev_len = len;
			}
			mCursorPos = i;
		}

		return true;
	}

	void EditBox::UpdateAt( float dt )
	{
		mBlink += int(dt*1000.0f);
	}

	///************************************************************************/
	/// TextBox
	///************************************************************************/

	TextBox::TextBox() : TextColor(ColorRGBA::White()), VerticalAlign(Align::Top), HorizontalAlign(Align::Left), ScrollOnOverflow(false), mLabel()
	{

	}

	void TextBox::SetFont( const Font& font )
	{
		mFont = font;
		Recalculate();
	}

	void TextBox::SetLabel( const char* text )
	{
		mLabel = text;
		Recalculate();
	}

	TextBox& TextBox::Set( Rect rect, const Font& font, const char* text /*= ""*/ )
	{
		X = rect.P1.X;
		Y = rect.P1.Y;
		Width = rect.GetWidth();
		Height = rect.GetHeight();
		mFont = font;
		mLabel = text;
		Recalculate();

		return *this;
	}

	void TextBox::BuildFrom( GTL::Structures::DataNode* node )
	{
		Control::BuildFrom(node);

		if (const DataNode* font = node->GetElement("font"))
			NodeToObject(mFont, font);

		if (node->GetElementIfExists("text", &mInternalLabel))
			SetLabel(mInternalLabel.c_str());

		if (const DataNode* tc = node->GetElement("textcolor"))
			NodeToObject(TextColor, tc);

		if (const DataNode* va = node->GetElement("valign"))
		{
			if (va->IsString("left"))
				VerticalAlign = Align::Left;
			else if (va->IsString("right"))
				VerticalAlign = Align::Right;
			else if (va->IsString("center"))
				VerticalAlign = Align::Center;
			else
				Backend->LogWarning("Incorrect value of `valign` for TextBox: `%s`", va->ValueAsString("<not a string>"));
		}

		if (const DataNode* ha = node->GetElement("halign"))
		{
			if (ha->IsString("left"))
				HorizontalAlign = Align::Left;
			else if (ha->IsString("right"))
				HorizontalAlign = Align::Right;
			else if (ha->IsString("center"))
				HorizontalAlign = Align::Center;
			else
				Backend->LogWarning("Incorrect value of `halign` for TextBox: `%s`", ha->ValueAsString("<not a string>"));
		}

		if (const DataNode* scrl = node->GetElement("scrollable"))
		{
			if (scrl->ValueAsNumber() > 0)
				ScrollOnOverflow = true;
		}

		Recalculate();
	}

	void TextBox::Recalculate()
	{
		if (!mLabel || !mFont) return;

		mLines.clear();

		Line cl;
		cl.Start = mLabel;

		const char* buf = mLabel;
		const char* last_space = buf;

		while (*buf)
		{
			//int blen = GTL::Strings::UTF8::ByteWidth(buf);
			float char_px_len = mFont->GetTextWidth(buf, 1);

			if (cl.PXLen + char_px_len > Width || *buf == 10)
			{
				cl.PXLen = 0;
				cl.CPLen = 0;

				if (*buf == 10)
				{
					last_space = buf;
					cl.IsWrapped = false;
				}
				else
					cl.IsWrapped = true;
				
				if (cl.Start == last_space)
					last_space = buf;

				for (const char* i=cl.Start; i<last_space; GTL::Strings::UTF8::Next(i, &i))
				{
					cl.PXLen += mFont->GetTextWidth(i, 1);
					cl.CPLen++;
				}

				mLines.push_back(cl);

				cl.Start = last_space+1;
				if (cl.PXLen + mFont->GetTextWidth(buf, 1) > Width)
					cl.Start = last_space;
				cl.PXLen = 0;

				buf = last_space+1;

				continue;
			}
			else
			{
				cl.PXLen += mFont->GetTextWidth(buf, 1);
			}

			if (*buf == ' ')
			{
				last_space = buf;
			}

			GTL::Strings::UTF8::Next(buf, &buf);
		}

		cl.CPLen = (int)GTL::Strings::UTF8::GetLength(cl.Start);
		cl.PXLen = mFont->GetTextWidth(cl.Start, cl.CPLen);
		mLines.push_back(cl);
	}

	void TextBox::DrawAt( float x, float y, const ColorRGBA& color )
	{
		if (!mFont) return;

		float h = mFont->GetHeight();

		Rect def = Backend->GetClippingRect();
		Backend->SetClippingRect(Rect(x,y,x+Width,y+Height));

		for (size_t i=0; i<mLines.size(); i++)
		{
			Line& l = mLines[i];

			if (HorizontalAlign == Align::Left)
				mFont->DrawText(Point(x,y+i*h), l.Start, color*TextColor, l.CPLen);
			else if (HorizontalAlign == Align::Center)
				mFont->DrawText(Point(x+(Width-l.PXLen)/2,y+i*h), l.Start, color*TextColor, l.CPLen);
			else if (HorizontalAlign == Align::Right)
				mFont->DrawText(Point(x+(Width-l.PXLen),y+i*h), l.Start, color*TextColor, l.CPLen);
		}

		Backend->SetClippingRect(def);
	}


	///************************************************************************/
	///* Control resource                                                     */
	///************************************************************************/

	void ControlResource::LoadFrom( const char* src )
	{
		ctrl = Control::CreateControlFromFile(src);
		if (!ctrl)
			Backend->LogError("Could not load control from `%s`", src);

		if (!strcmp(ParentProducible->GetName(), "Mode"))
		{
			Mode* mode = (Mode*)ParentProducible;
			mode->MainGUI.AddChild(ctrl);
		} else
			Backend->LogError("Resource `%s` not in Mode!", src);
	}

	///************************************************************************/
	///* Immediate mode GUI                                                   */
	///************************************************************************/

	static struct
	{
		intptr_t hotitem;
		intptr_t activeitem;
	} IMGUIState = {0, 0};

	static inline void IMDraw(float x, float y, float w, float h, float sx, float sy, float sw, float sh, const ImageRef* img)
	{
		img->Draw(Rect(sx,sy,sx+sw,sy+sh), Rect(x,y,x+w,h+y));
	}
	static inline void IMButDraw(float x, float y, float w, float sth, float s, const ImageRef* img)
	{
		img->Draw(Rect(0,s*sth,w,s*sth+sth), Rect(x,y,x+w,sth+y));
	}

	static int IMRegionHit(float x, float y, float w, float h)
	{
		if (Backend->MouseState->X < x ||
			Backend->MouseState->Y < y ||
			Backend->MouseState->X >= x + w ||
			Backend->MouseState->Y >= y + h)
			return 0;
		return 1;
	}

	int IM::Button( intptr_t ID, float x, float y, const ImageRef* img, const char* text, const FontRef* font, const ColorRGBA& col )
	{
		float sth = img->Height/Button::ButtonState::TotalStates;
		float w = img->Width;
		if (IMRegionHit(x, y, w, sth))
		{
			IMGUIState.hotitem = ID;
			if (IMGUIState.activeitem == 0 && Backend->MouseState->Buttons&1)
				IMGUIState.activeitem = ID;
		}

		if (IMGUIState.activeitem == ID)
		{
			IMButDraw(x, y, w, sth, Button::ButtonState::Pressed, img);
		} else if (IMGUIState.hotitem == ID)
		{
			IMButDraw(x, y, w, sth, Button::ButtonState::MouseOver, img);
		} else
			IMButDraw(x, y, w, sth, Button::ButtonState::Normal, img);

		if (text && font)
		{
			float len = font->GetTextWidth(text)/2;
			float fh = font->GetHeight()/2;
			if (IMGUIState.activeitem == ID)
			{
				x += 1.0f;
				y += 1.0f;
			}
			font->DrawText(Point((x+w/2)-len, (y+sth/2)-fh), text, col);
		}

		if ((Backend->MouseState->Buttons&1) == 0 && 
			IMGUIState.hotitem == ID && 
			IMGUIState.activeitem == ID)
			return 1;

		return 0;
	}

	void IM::Start()
	{
		IMGUIState.hotitem = 0;
	}

	void IM::End()
	{
		if ((Backend->MouseState->Buttons&1) == 0 )
		{
			IMGUIState.activeitem = 0;
		}
		else
		{
			if (IMGUIState.activeitem == 0)
				IMGUIState.activeitem = -1;
		}
	}
}}}